package com.ydd.aop;


import com.ydd.annotation.AutoFill;
import com.ydd.constant.AutoFillConstant;
import com.ydd.context.BaseContext;
import com.ydd.enumeration.OperationType;
import com.ydd.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @Description TODO
 * @Author itheima
 * @Date 2023-04-15 09:49
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 对mapper包下所有类中的所有方法和使用自定义注解@AutoFill的方法
     */
    @Pointcut("execution(* com.ydd.mapper.*.*(..)) && @annotation(com.ydd.annotation.AutoFill)")
    public void pt(){}

    @Before("pt()")
    public void autoFill(JoinPoint jp){

        try {
            log.info("进行公共字段填充：{}" + jp);

            //省略公共字段填充的具体操作
            //1.获取原始方法上的注解对象@AutoFill
            // 提示：获取原始方法对象：(MethodSignature)jp.getSignature().getMethod()
            // 提示：获取原始方法对象上面的自定义注解对象： 原始方法对象.getAnnotation()
            MethodSignature signature = (MethodSignature)jp.getSignature();

            AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);

            //2.根据@AutoFill获取value属性值
            OperationType operationType = autoFill.value();

            //3.根据JoinPoint获取原始方法参数数据
            Object[] args = jp.getArgs();

            //4.判断原始方法参数数组是否有效，如果无效结束返回
            if(args==null || args.length==0){
                return;
            }

            //5.如果原始方法参数数组有效，获取第一个参数对象（填充字段的所属对象）
            Object obj = args[0];

            //6.根据参数对象获取字节码对象Class
            Class clazz = obj.getClass();

            //7.根据AutoFill获取value属性值判断是新增操作（填充4个字段）还是修改操作（填充2个字段）
            //8.修改填充
            Method setUpdateTimeMethod = clazz.getMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);
            Method setUpdateUserMethod = clazz.getMethod(AutoFillConstant.SET_UPDATE_USER,Long.class);
            setUpdateTimeMethod.invoke(obj, LocalDateTime.now());
            setUpdateUserMethod.invoke(obj, BaseContext.getCurrentId());
            //9.新增填充
            if(operationType == OperationType.INSERT) {
                //9.1 根据节码对象Class获取公共字段的set反射方法
                Method setCreateTimeMethod = clazz.getMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);
                Method setCreateUserMethod = clazz.getMethod(AutoFillConstant.SET_CREATE_USER,Long.class);
                //9.2 执行公共字段的set反射方法，填充数据
                //   提示执行set方法语法（非反射）：参数对象.set属性封装方法(填充的数据)
                //   提示执行set方法语法（反射）：set反射方法.invoke(方法的所属对象，参数数据)；
                setCreateTimeMethod.invoke(obj, LocalDateTime.now());
                setCreateUserMethod.invoke(obj, BaseContext.getCurrentId());
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(e.getMessage());
        }

    }
}
